Skip to content

feat: Update Sana Canvas Buttons#4000

Open
josh-bagwell wants to merge 47 commits into
Workday:prerelease/majorfrom
josh-bagwell:feat/update-buttons-sana-canvas-26-06
Open

feat: Update Sana Canvas Buttons#4000
josh-bagwell wants to merge 47 commits into
Workday:prerelease/majorfrom
josh-bagwell:feat/update-buttons-sana-canvas-26-06

Conversation

@josh-bagwell

@josh-bagwell josh-bagwell commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes: #3976

Release Category

Components

Release Note

This updates our Button styling to introduce Sana Canvas.

BREAKING CHANGES

Visual Changes


Where Should the Reviewer Start?

/modules/react/button

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added an inverse variant to DeleteButton, including Storybook/MDX examples and visual-testing state permutations.
    • Updated the Canvas Kit 16.0 Upgrade Guide to document the new DeleteButton inverse variant.
  • Style

    • Refined button sizing/spacing (including extra-small inline padding logic and icon-position padding), and adjusted base button font weight.
    • Updated Primary/Secondary/Tertiary hover, active, focus-visible, disabled, and inverse styling behavior.
    • Updated colorSpace.hover() / colorSpace.pressed() to derive values from legacy tokens.
  • Bug Fixes

    • Improved SegmentedControl.Item layout by honoring content min-width (min-content).
  • Tests

    • Expanded colorSpace hover/pressed coverage for colorType (accent vs surface).

@josh-bagwell josh-bagwell requested a review from a team as a code owner June 15, 2026 22:49
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR updates shared button styling tokens, adds inverse DeleteButton support with story and docs coverage, changes colorSpace overlay token selection, adds upgrade guide documentation, and adds minWidth: 'min-content' to SegmentedControlItem.

Changes

Button styling and inverse DeleteButton

Layer / File(s) Summary
Shared overlay tokens
modules/styling/lib/colorSpace.tsx, modules/styling/spec/colorSpace.spec.ts
colorSpace.hover() and colorSpace.pressed() now read legacy mixin color and opacity tokens, and the spec asserts the updated accent and surface outputs.
BaseButton sizing
modules/react/button/lib/BaseButton.tsx
BaseButton adds calc, updates size padding and minWidth values, swaps icon-position padding assignments, and changes the base font weight.
Primary, Secondary, and Tertiary states
modules/react/button/lib/PrimaryButton.tsx, modules/react/button/lib/SecondaryButton.tsx, modules/react/button/lib/TertiaryButton.tsx
PrimaryButton, SecondaryButton, and TertiaryButton update hover, active, focus, disabled, and inverse styling tokens, and TertiaryButton passes iconPosition directly into its stencil.
DeleteButton inverse variant
modules/react/button/lib/DeleteButton.tsx, modules/react/button/stories/button/examples/DeleteInverse.tsx, modules/react/button/stories/button/Button.stories.tsx, modules/react/button/stories/button/Button.mdx, modules/react/button/stories/visual-testing/DeleteButton.stories.tsx
DeleteButtonProps adds variant?: 'inverse', default hover and active backgrounds switch to colorSpace.hover() and colorSpace.pressed(), deleteButtonStencil adds an inverse modifier, and DeleteButton forwards variant into the stencil. The new inverse example, story, docs, and visual permutations are added.

Upgrade guide documentation

Layer / File(s) Summary
Upgrade guide content
modules/docs/llm/upgrade-guides/16.0-UPGRADE-GUIDE.md, modules/docs/mdx/16.0-UPGRADE-GUIDE.mdx
The new 16.0 upgrade guide adds overview, codemod, new utility, deprecation, glossary, and codemod reference content, and the MDX guide replaces the buttons placeholder with the DeleteButton inverse variant entry.

Segmented control sizing

Layer / File(s) Summary
SegmentedControlItem min width
modules/react/segmented-control/lib/SegmentedControlItem.tsx
segmentedControlItemStencil adds minWidth: 'min-content' to its base styles.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • RayRedGoose
  • alanbsmith

Poem

🐰 I hop through tokens, neat and bright,
Inverse buttons bloom in storybook light.
A tiny min-width makes layout spring,
And button colors do their rabbit thing.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Fails #3976: shared padding/font updates land, but inverse variants remain and DeleteButton adds inverse instead of the requested secondary variant. Remove inverse from Primary/Secondary/Tertiary via codemod and add DeleteButton variant="secondary" with the requested styling.
Out of Scope Changes check ⚠️ Warning The SegmentedControl minWidth change is unrelated to #3976's button styling and API updates. Move the SegmentedControl tweak to a separate PR or drop it from this button-focused change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: updating Canvas button styles for Sana Canvas.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/react/button/lib/DeleteButton.tsx`:
- Around line 14-19: The PR objective specifies a `secondary` variant for
DeleteButton, but the current implementation uses `inverse` across all files,
creating a mismatch between intent and implementation. Align all layers to use
the intended variant name consistently: in
modules/react/button/lib/DeleteButton.tsx lines 14-19, change the
DeleteButtonProps.variant type union from 'inverse' to the intended variant
(e.g., 'secondary'); in modules/react/button/lib/DeleteButton.tsx lines 80-119,
update the modifier block logic to use the intended variant key instead of
'inverse'; in modules/react/button/stories/button/examples/DeleteInverse.tsx
lines 15-24, rename the example component to reflect the intended variant name;
in modules/react/button/stories/button/Button.stories.tsx lines 6-7, update the
import statement to use the renamed example component; in
modules/react/button/stories/button/Button.stories.tsx lines 46-48, update both
the story export name and render target to reference the intended variant; in
modules/react/button/stories/button/Button.mdx lines 12-13, update the import to
reference the renamed example; in modules/react/button/stories/button/Button.mdx
lines 86-90, update all prose references and example blocks to use the intended
variant guidance; and in
modules/react/button/stories/visual-testing/DeleteButton.stories.tsx lines
30-33, update the permutation values and labels to use the intended variant
contract throughout.
- Line 142: The deleteButtonStencil call on line 142 in DeleteButton.tsx is
missing the grow parameter, while PrimaryButton and SecondaryButton both pass it
through to their respective stencil calls. Since deleteButtonStencil extends
buttonStencil which defines the grow modifier, omitting it causes the grow
styling to be silently dropped. Add the grow parameter to the
deleteButtonStencil function call on line 142, passing it alongside the variant
and size parameters to maintain consistency with the other button components.

In `@modules/react/button/lib/SecondaryButton.tsx`:
- Around line 27-33: The SecondaryButton's default state at lines 27-33 uses a
transparent border which relies too heavily on the hover state's border color
change for visual differentiation, creating insufficient perceivable change for
low-vision users. Enhance the default state affordance by either adding a
non-transparent subtle border color, introducing a subtle background shift, or
applying another non-color visual cue to the default state styling. Ensure the
hover state styling (lines 39-43) maintains sufficient contrast and visual
distinction from the enhanced default state. After making these changes,
validate the contrast ratios and state distinguishability during the visual
accessibility review to confirm adequate perceivable differentiation exists
between default and hover states.

In `@modules/react/button/lib/TertiaryButton.tsx`:
- Around line 48-57: The TertiaryButton component in the hover and active state
blocks still includes textDecoration: 'underline', which conflicts with the v16
behavior change objective to remove underlines in those states. Remove the
textDecoration: 'underline' line from both the '&:hover, &.hover' block and the
'&:active, &.active' block to align with the intended styling update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a786b8ad-b7b5-4af3-821e-6612dabc325d

📥 Commits

Reviewing files that changed from the base of the PR and between 70f2ee9 and 72f302f.

📒 Files selected for processing (8)
  • modules/react/button/lib/BaseButton.tsx
  • modules/react/button/lib/DeleteButton.tsx
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/TertiaryButton.tsx
  • modules/react/button/stories/button/Button.mdx
  • modules/react/button/stories/button/Button.stories.tsx
  • modules/react/button/stories/button/examples/DeleteInverse.tsx
  • modules/react/button/stories/visual-testing/DeleteButton.stories.tsx

Comment on lines +14 to +19
export interface DeleteButtonProps extends ButtonProps {
/**
* Variant has an option for `inverse` which will inverse the styling
*/
variant?: 'inverse';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Variant contract appears misaligned with the stated PR objective (secondary vs inverse).

The PR objective states DeleteButton should add a secondary variant, but these changes introduce inverse across API, styling, docs, stories, and visual permutations. Please confirm intended product contract, then align all layers consistently.

  • modules/react/button/lib/DeleteButton.tsx#L14-L19: Update DeleteButtonProps.variant union to the intended variant(s).
  • modules/react/button/lib/DeleteButton.tsx#L80-L119: Rename/rework modifier block to the intended variant key and token behavior.
  • modules/react/button/stories/button/examples/DeleteInverse.tsx#L15-L24: Rename/replace example component to match the intended variant.
  • modules/react/button/stories/button/Button.stories.tsx#L6-L7: Update import to the intended example name.
  • modules/react/button/stories/button/Button.stories.tsx#L46-L48: Update exported story name/render target to intended variant.
  • modules/react/button/stories/button/Button.mdx#L12-L13: Update MDX import for intended variant example.
  • modules/react/button/stories/button/Button.mdx#L86-L90: Update prose/example block to intended variant guidance.
  • modules/react/button/stories/visual-testing/DeleteButton.stories.tsx#L30-L33: Update permutation values/labels to intended variant contract.
📍 Affects 5 files
  • modules/react/button/lib/DeleteButton.tsx#L14-L19 (this comment)
  • modules/react/button/lib/DeleteButton.tsx#L80-L119
  • modules/react/button/stories/button/examples/DeleteInverse.tsx#L15-L24
  • modules/react/button/stories/button/Button.stories.tsx#L6-L7
  • modules/react/button/stories/button/Button.stories.tsx#L46-L48
  • modules/react/button/stories/button/Button.mdx#L12-L13
  • modules/react/button/stories/button/Button.mdx#L86-L90
  • modules/react/button/stories/visual-testing/DeleteButton.stories.tsx#L30-L33
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/DeleteButton.tsx` around lines 14 - 19, The PR
objective specifies a `secondary` variant for DeleteButton, but the current
implementation uses `inverse` across all files, creating a mismatch between
intent and implementation. Align all layers to use the intended variant name
consistently: in modules/react/button/lib/DeleteButton.tsx lines 14-19, change
the DeleteButtonProps.variant type union from 'inverse' to the intended variant
(e.g., 'secondary'); in modules/react/button/lib/DeleteButton.tsx lines 80-119,
update the modifier block logic to use the intended variant key instead of
'inverse'; in modules/react/button/stories/button/examples/DeleteInverse.tsx
lines 15-24, rename the example component to reflect the intended variant name;
in modules/react/button/stories/button/Button.stories.tsx lines 6-7, update the
import statement to use the renamed example component; in
modules/react/button/stories/button/Button.stories.tsx lines 46-48, update both
the story export name and render target to reference the intended variant; in
modules/react/button/stories/button/Button.mdx lines 12-13, update the import to
reference the renamed example; in modules/react/button/stories/button/Button.mdx
lines 86-90, update all prose references and example blocks to use the intended
variant guidance; and in
modules/react/button/stories/visual-testing/DeleteButton.stories.tsx lines
30-33, update the permutation values and labels to use the intended variant
contract throughout.

grow={grow}
iconPosition={iconPosition}
cs={[deleteButtonStencil({size, iconPosition}), cs]}
cs={[deleteButtonStencil({variant, size, iconPosition}), cs]}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether `grow` is a stencil modifier in button base and compare call-site parity.
set -euo pipefail

echo "== Find grow-related stencil modifier definitions =="
rg -n -C3 '\bgrow\b' modules/react/button/lib/BaseButton.tsx modules/react/button/lib/Button.tsx

echo
echo "== Compare stencil invocation patterns across button components =="
rg -n -C2 'Stencil\(\{[^}]*grow|deleteButtonStencil\(\{|secondaryButtonStencil\(\{|tertiaryButtonStencil\(\{' modules/react/button/lib

Repository: Workday/canvas-kit

Length of output: 3625


🏁 Script executed:

# Check the deleteButtonStencil definition and its modifier structure
rg -n -A30 'export.*deleteButtonStencil|const deleteButtonStencil' modules/react/button/lib/DeleteButton.tsx | head -50

Repository: Workday/canvas-kit

Length of output: 1464


Pass grow into deleteButtonStencil to keep variant parity with other buttons.

On line 142, DeleteButton omits grow in the stencil call while PrimaryButton and SecondaryButton pass it through. Since deleteButtonStencil extends buttonStencil (which defines the grow modifier), this inconsistency silently drops grow styling for DeleteButton.

Suggested fix
-        cs={[deleteButtonStencil({variant, size, iconPosition}), cs]}
+        cs={[deleteButtonStencil({variant, size, iconPosition, grow}), cs]}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/DeleteButton.tsx` at line 142, The
deleteButtonStencil call on line 142 in DeleteButton.tsx is missing the grow
parameter, while PrimaryButton and SecondaryButton both pass it through to their
respective stencil calls. Since deleteButtonStencil extends buttonStencil which
defines the grow modifier, omitting it causes the grow styling to be silently
dropped. Add the grow parameter to the deleteButtonStencil function call on line
142, passing it alongside the variant and size parameters to maintain
consistency with the other button components.

Comment thread modules/react/button/lib/SecondaryButton.tsx Outdated
Comment thread modules/react/button/lib/TertiaryButton.tsx Outdated
@josh-bagwell josh-bagwell changed the title Feat/update buttons sana canvas 26 06 feat: Update Sana Canvas Buttons Jun 15, 2026
@cypress

cypress Bot commented Jun 15, 2026

Copy link
Copy Markdown

Workday/canvas-kit    Run #11149

Run Properties:  status check passed Passed #11149  •  git commit ea92e7475c ℹ️: Merge 48848b33bfddf561e22e86452ed931618e991cee into 32383b89220921afef6a3c8a90a3...
Project Workday/canvas-kit
Branch Review feat/update-buttons-sana-canvas-26-06
Run status status check passed Passed #11149
Run duration 02m 31s
Commit git commit ea92e7475c ℹ️: Merge 48848b33bfddf561e22e86452ed931618e991cee into 32383b89220921afef6a3c8a90a3...
Committer Josh
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 17
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 809
View all changes introduced in this branch ↗︎
UI Coverage  19.51%
  Untested elements 1541  
  Tested elements 371  
Accessibility  99.41%
  Failed rules  5 critical   5 serious   0 moderate   2 minor
  Failed elements 72  

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/react/button/lib/TertiaryButton.tsx (1)

204-214: ⚠️ Potential issue | 🟡 Minor

iconPosition is not derived before being passed to the stencil, causing compound modifiers to not apply.

When a consumer renders <TertiaryButton icon={someIcon}>Label</TertiaryButton> without an explicit iconPosition, the undefined value is passed directly to tertiaryButtonStencil. Although the Button component internally derives baseIconPosition = 'start' (for icon + text), that derived value is never used by the stencil, which expects explicit 'start', 'end', or 'only' values to match its compound modifiers (lines 98-179).

The base ButtonStencil has identical compound modifier patterns but with different padding values. Since tertiaryButtonStencil padding does not apply when iconPosition is undefined, buttons with icon and label will use only the base button padding (e.g., md/sm for medium size with 'start') instead of the tertiary-specific padding (e.g., xs/sm). This creates a visual inconsistency.

Derive baseIconPosition in TertiaryButton before passing it to the stencil, matching the logic in Button.tsx (lines 50-56):

const baseIconPosition = iconPosition
  ? iconPosition
  : icon
    ? children
      ? 'start'
      : 'only'
    : undefined;

// Then pass baseIconPosition to stencil instead of iconPosition
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/TertiaryButton.tsx` around lines 204 - 214, The
iconPosition parameter is being passed directly to tertiaryButtonStencil without
deriving its value first, causing compound modifiers to not apply when
iconPosition is undefined. Derive a baseIconPosition variable in TertiaryButton
using the same logic from Button.tsx (lines 50-56): check if iconPosition is
explicitly provided, otherwise check if icon exists and whether children exist
to determine if the position should be 'start' (icon with text), 'only' (icon
without text), or undefined. Then pass this derived baseIconPosition to
tertiaryButtonStencil instead of the raw iconPosition parameter in the stencil
call around line 204-214.
🧹 Nitpick comments (1)
modules/react/button/lib/TertiaryButton.tsx (1)

3-3: 💤 Low value

Unused import: px2rem is not referenced in this file.

The px2rem utility is imported but not used anywhere in the component. Consider removing it to keep imports clean.

-import {createStencil, cssVar, px2rem} from '`@workday/canvas-kit-styling`';
+import {createStencil, cssVar} from '`@workday/canvas-kit-styling`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/TertiaryButton.tsx` at line 3, Remove the unused
`px2rem` import from the import statement at the top of the TertiaryButton.tsx
file. The import statement should only include `createStencil` and `cssVar` from
'`@workday/canvas-kit-styling`', since `px2rem` is not used anywhere in the
component.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@modules/react/button/lib/TertiaryButton.tsx`:
- Around line 204-214: The iconPosition parameter is being passed directly to
tertiaryButtonStencil without deriving its value first, causing compound
modifiers to not apply when iconPosition is undefined. Derive a baseIconPosition
variable in TertiaryButton using the same logic from Button.tsx (lines 50-56):
check if iconPosition is explicitly provided, otherwise check if icon exists and
whether children exist to determine if the position should be 'start' (icon with
text), 'only' (icon without text), or undefined. Then pass this derived
baseIconPosition to tertiaryButtonStencil instead of the raw iconPosition
parameter in the stencil call around line 204-214.

---

Nitpick comments:
In `@modules/react/button/lib/TertiaryButton.tsx`:
- Line 3: Remove the unused `px2rem` import from the import statement at the top
of the TertiaryButton.tsx file. The import statement should only include
`createStencil` and `cssVar` from '`@workday/canvas-kit-styling`', since `px2rem`
is not used anywhere in the component.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0540207b-8d15-4719-9b1d-15792a8765c5

📥 Commits

Reviewing files that changed from the base of the PR and between 72f302f and 3a364dd.

📒 Files selected for processing (3)
  • modules/react/button/lib/BaseButton.tsx
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/TertiaryButton.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/BaseButton.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
modules/react/button/lib/SecondaryButton.tsx (1)

3-3: ⚡ Quick win

Remove unused colorSpace import.

The colorSpace import is no longer used after removing the colorSpace.darken() calls from hover/active states.

♻️ Suggested fix
-import {colorSpace, createStencil, cssVar} from '`@workday/canvas-kit-styling`';
+import {createStencil, cssVar} from '`@workday/canvas-kit-styling`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/SecondaryButton.tsx` at line 3, Remove the unused
`colorSpace` import from the import statement in SecondaryButton.tsx. The
`colorSpace` module is no longer referenced in the code after the
`colorSpace.darken()` calls were removed from the hover and active states. Keep
the other imports `createStencil` and `cssVar` which are still in use. Simply
delete `colorSpace` from the destructured import list from
'`@workday/canvas-kit-styling`'.
modules/react/button/lib/TertiaryButton.tsx (1)

1-1: ⚡ Quick win

Remove unused focusRing import.

The focusRing import is no longer used after refactoring focus styles to use explicit boxShadowInner/boxShadowOuter values.

♻️ Suggested fix
-import {createComponent, focusRing} from '`@workday/canvas-kit-react/common`';
+import {createComponent} from '`@workday/canvas-kit-react/common`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/TertiaryButton.tsx` at line 1, Remove the unused
`focusRing` import from the import statement at the top of TertiaryButton.tsx.
The `focusRing` symbol is no longer needed since the focus styles have been
refactored to use explicit `boxShadowInner` and `boxShadowOuter` values instead.
Keep the `createComponent` import and remove only `focusRing` from the
destructured imports coming from '`@workday/canvas-kit-react/common`'.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@modules/react/button/lib/SecondaryButton.tsx`:
- Line 3: Remove the unused `colorSpace` import from the import statement in
SecondaryButton.tsx. The `colorSpace` module is no longer referenced in the code
after the `colorSpace.darken()` calls were removed from the hover and active
states. Keep the other imports `createStencil` and `cssVar` which are still in
use. Simply delete `colorSpace` from the destructured import list from
'`@workday/canvas-kit-styling`'.

In `@modules/react/button/lib/TertiaryButton.tsx`:
- Line 1: Remove the unused `focusRing` import from the import statement at the
top of TertiaryButton.tsx. The `focusRing` symbol is no longer needed since the
focus styles have been refactored to use explicit `boxShadowInner` and
`boxShadowOuter` values instead. Keep the `createComponent` import and remove
only `focusRing` from the destructured imports coming from
'`@workday/canvas-kit-react/common`'.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e259c913-e45b-4c79-a787-d8e8f4692428

📥 Commits

Reviewing files that changed from the base of the PR and between 79482da and f80148a.

📒 Files selected for processing (6)
  • modules/react/button/lib/BaseButton.tsx
  • modules/react/button/lib/DeleteButton.tsx
  • modules/react/button/lib/PrimaryButton.tsx
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/TertiaryButton.tsx
  • modules/styling/lib/colorSpace.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • modules/react/button/lib/BaseButton.tsx
  • modules/react/button/lib/DeleteButton.tsx

@josh-bagwell josh-bagwell added the ready for review Code is ready for review label Jun 23, 2026
@RayRedGoose RayRedGoose added the needs-a11y Extra attention from accessibility is needed label Jun 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
modules/styling/spec/colorSpace.spec.ts (1)

29-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add explicit surfaceType: 'inverse' assertions for hover/pressed.

Line 29 and Line 54 currently validate only the default surface branch. Since colorSpace now branches on surfaceType, add inverse-path expectations too to prevent token-regression gaps.

Also applies to: 54-63

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/styling/spec/colorSpace.spec.ts` around lines 29 - 41, The hover and
pressed specs in colorSpace.spec.ts only cover the default surface branch, so
add explicit assertions for the inverse surface path as well. Update the
existing colorSpace.hover and colorSpace.pressed test cases to include a
`surfaceType: 'inverse'` variant and verify the expected token strings for that
branch, using the same `colorSpace` helper and token placeholders already in the
spec.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/react/button/lib/PrimaryButton.tsx`:
- Around line 88-91: The hover and active state colorSpace calls in
PrimaryButton are using the default surface overlay path instead of the inverse
variant, which can produce incorrect overlays for inverse buttons. Update the
affected colorSpace.hover and colorSpace.active usages in PrimaryButton to pass
surfaceType: 'inverse' alongside the existing color and colorType arguments so
the inverse state styling resolves correctly.

---

Nitpick comments:
In `@modules/styling/spec/colorSpace.spec.ts`:
- Around line 29-41: The hover and pressed specs in colorSpace.spec.ts only
cover the default surface branch, so add explicit assertions for the inverse
surface path as well. Update the existing colorSpace.hover and
colorSpace.pressed test cases to include a `surfaceType: 'inverse'` variant and
verify the expected token strings for that branch, using the same `colorSpace`
helper and token placeholders already in the spec.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fe106b54-766a-4ed4-bc99-4825bf6a70d3

📥 Commits

Reviewing files that changed from the base of the PR and between 79482da and dedcd78.

📒 Files selected for processing (7)
  • modules/react/button/lib/BaseButton.tsx
  • modules/react/button/lib/DeleteButton.tsx
  • modules/react/button/lib/PrimaryButton.tsx
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/TertiaryButton.tsx
  • modules/styling/lib/colorSpace.tsx
  • modules/styling/spec/colorSpace.spec.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • modules/styling/lib/colorSpace.tsx
  • modules/react/button/lib/DeleteButton.tsx
  • modules/react/button/lib/SecondaryButton.tsx
  • modules/react/button/lib/TertiaryButton.tsx

Comment on lines +88 to +91
[buttonStencil.vars.background]: colorSpace.hover({
color: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
mixinColor: system.legacy.color.accent.overlay.mixin,
mixinValue: system.legacy.opacity.accent.hover,
fallback: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
colorType: 'surface',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass surfaceType: 'inverse' in inverse hover/active colorSpace calls.

Line 88 and Line 110 currently use the default surface overlay branch, so inverse buttons can resolve incorrect state overlays.

Suggested fix
         '&:hover, &.hover': {
           [buttonStencil.vars.background]: colorSpace.hover({
             color: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
             fallback: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
             colorType: 'surface',
+            surfaceType: 'inverse',
           }),
         '&:active, &.active': {
           [buttonStencil.vars.background]: colorSpace.pressed({
             color: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
             fallback: cssVar(brand.action.lightest, system.legacy.color.surface.inverse),
             colorType: 'surface',
+            surfaceType: 'inverse',
           }),

Also applies to: 110-113

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/react/button/lib/PrimaryButton.tsx` around lines 88 - 91, The hover
and active state colorSpace calls in PrimaryButton are using the default surface
overlay path instead of the inverse variant, which can produce incorrect
overlays for inverse buttons. Update the affected colorSpace.hover and
colorSpace.active usages in PrimaryButton to pass surfaceType: 'inverse'
alongside the existing color and colorType arguments so the inverse state
styling resolves correctly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/styling/lib/colorSpace.tsx (1)

83-98: 🎯 Functional Correctness | 🔴 Critical

Correct implementation and tests must be updated to address mismatch.

The InteractiveStateProps interface correctly removed surfaceType, but the function implementation in modules/styling/lib/colorSpace.tsx now resolves tokens to system.legacy.color[colorType].overlay.mixin (a single generic color) rather than the specific surfaceType-suffixed tokens expected by tests.

This results in:

  1. Token Regression: The CSS generated no longer differentiates between default and inverse surface variants, collapsing them to --cnvs-sys-color-surface-overlay-mixin.
  2. Failing Tests: modules/styling/spec/colorSpace.spec.ts (lines 33-39, 58-64) explicitly passes the undefined surfaceType property and expects the old token structure (e.g., --cnvs-sys-color-surface-overlay-hover-default).
  3. Type Safety: Callers passing surfaceType will encounter TypeScript errors if InteractiveStateProps remains the interface.

The implementation logic should either be updated to support the specific token suffixes if distinct behavior is required, or the tests/documentation must be updated to remove surfaceType arguments and update expected output strings to match the generic overlay-mixin token being used.

Comparison

Current Implementation Output:

color-mix(in srgb, var(--cnvs-sys-color-brand-accent-primary, var(--cnvs-brand-primary-base)) , var(--cnvs-sys-color-surface-overlay-mixin, var(black)) calc(var(--cnvs-sys-opacity-surface-hover, 0) * 100%))

Test File Expectation (lines 38, 63):

// Expects distinct hover vs pressed suffixes and default suffix
var(--cnvs-sys-color-surface-overlay-hover-default, black)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/styling/lib/colorSpace.tsx` around lines 83 - 98, The interactive
color token resolution in colorSpace.tsx no longer matches the spec expectations
because it now uses the generic surface overlay mixin token instead of
surfaceType-specific suffixes. Update the implementation around the
InteractiveStateProps-driven color mix logic to either restore distinct
default/inverse surface token handling or align the tests and docs with the new
generic overlay-mixin behavior. Also update
modules/styling/spec/colorSpace.spec.ts to remove surfaceType usage and assert
the new resolved token strings consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/styling/lib/colorSpace.tsx`:
- Around line 113-120: The hover and pressed state color helpers are both using
the wrong overlay token path, so update the state-specific mixin selection in
the interactive color helpers in colorSpace.tsx: have hover resolve from
system.legacy.color[colorType].overlay.hover and pressed resolve from
system.legacy.color[colorType].overlay.pressed instead of overlay.mixin, keeping
the existing darken call structure intact so the generated CSS variables match
the spec and the hover/pressed token names remain distinct.

---

Outside diff comments:
In `@modules/styling/lib/colorSpace.tsx`:
- Around line 83-98: The interactive color token resolution in colorSpace.tsx no
longer matches the spec expectations because it now uses the generic surface
overlay mixin token instead of surfaceType-specific suffixes. Update the
implementation around the InteractiveStateProps-driven color mix logic to either
restore distinct default/inverse surface token handling or align the tests and
docs with the new generic overlay-mixin behavior. Also update
modules/styling/spec/colorSpace.spec.ts to remove surfaceType usage and assert
the new resolved token strings consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 13bafb38-215e-4b5a-a075-c0c9ffab587f

📥 Commits

Reviewing files that changed from the base of the PR and between dedcd78 and 17dfcc6.

📒 Files selected for processing (1)
  • modules/styling/lib/colorSpace.tsx

Comment thread modules/react/button/lib/PrimaryButton.tsx Outdated
Comment thread modules/react/button/lib/SecondaryButton.tsx
// Hover Styles
'&:hover, &.hover': {
[buttonStencil.vars.background]: colorSpace.darken({
[buttonStencil.vars.background]: colorSpace.hover({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to PrimaryButton, wondering if the ThemeOverrides story content is still fully accurate and worth keeping. I noticed the "Override Primary Color Via Canvas Provider" story renders PrimaryButtons that also don't show a hover color.

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is an issue with removing the data-theme from tokens.

@sheelah sheelah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this tricky one! Left a few comments inline.

{
variant: [
{value: undefined, label: ''},
{value: 'inverse', label: 'Inverse'},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to wrap the inverse variant in a container with a dark background, for consistency with the other button components' inverse variant visual tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I can update this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did but, I'm not sure where this variant should exist?

I feel like this is now an accessibility issue with a dark background.

Screenshot 2026-06-26 at 5 33 57 PM

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah agreed -- think we should bring this up with design.

@RayRedGoose RayRedGoose left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. Left some small notes, but it's a good candidate to get merge once a11y review is done 👍

Comment on lines +7 to +10
## Why You Should Upgrade

Soon to be...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep it for now

Comment thread modules/docs/llm/upgrade-guides/16.0-UPGRADE-GUIDE.md
Comment thread modules/react/button/lib/BaseButton.tsx Outdated
Comment thread modules/react/button/lib/BaseButton.tsx Outdated
Comment thread modules/react/button/stories/button/examples/DeleteInverse.tsx Outdated
Comment thread modules/react/button/stories/button/Button.mdx Outdated
Comment thread modules/docs/mdx/16.0-UPGRADE-GUIDE.mdx Outdated

### Buttons

#### Delete Button Inverse Variant

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we change to another version name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I just pushed an update for this.

It's now Outline.

<DeleteButton variant="outline">Delete</DeleteButton>

Comment thread modules/react/button/lib/PrimaryButton.tsx Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-a11y Extra attention from accessibility is needed ready for review Code is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants